using GameProtocols;
using System.Collections.Generic;

namespace Network 
{
    public class NetManager :Singleton<NetManager> 
    {
        public GameConnection tcpConc { private set; get; }
        public KcpConnection kcpConc { private set; get; }

        public MsgDispatcher msgDispatcher;

        public bool UdpLoopback;

        public NetManager()
        {
            tcpConc = new GameConnection();
            kcpConc = new KcpConnection();

            msgDispatcher = new MsgDispatcher();
            tcpConc.disconnectDelegate += ontcpDisconnect;
        }

        public void Connect(string ipAddr, int port)
        {
            tcpConc.Connect(ipAddr, port);
        }

        public void UdpConnect(string ipAddr, ushort port, uint SessionID)
        {
            kcpConc.Connect(ipAddr, port, SessionID);
        }

        public bool IsStable()
        {
            return tcpConc.isConnected && kcpConc.IsStable;
        }

        public void Tick(float deltaTime)
        {
            tcpConc.Tick(deltaTime);
            kcpConc.Tick(deltaTime);

            Queue<MsgObject> msgQueue = new Queue<MsgObject>();

            MsgObject msg = tcpConc.GetMsg(); 
            while(msg != null) 
            {
                
                msgQueue.Enqueue(msg); 
                msg = tcpConc.GetMsg();
            }

            msg = kcpConc.GetMsg();
            while(msg != null)
            {
                msgQueue.Enqueue(msg);
                msg = kcpConc.GetMsg();
            }

            msgDispatcher.process(msgQueue);
        }

        public void Destroy()
        {
            tcpConc.Reset(false);
            UdpClose();
        }

        public void UdpClose()
        {
            kcpConc.Close();
        }

        public void RegistMsg(ushort f_id, MsgCallback f_func)
        {
            msgDispatcher.RegistMsg(f_id, f_func);
        }

        public void UnregistMsg(ushort f_id, MsgCallback f_func = null)
        {
            msgDispatcher.UnregistMsg(f_id, f_func);
        }

        public void SendUdpMessage(ushort f_id, object content)
        {
            if(UdpLoopback)
            {
                MsgObject msg = new MsgObject(f_id, null, MsgType.PB);
                msg.m_deserializeBody = content;
                msgDispatcher.Dispatch(msg);
            }
            else
            {
                byte[] byteContent = content != null ? ProtoUtil.SerializeProto(content) : null;
                kcpConc.SendMessage(f_id, MsgType.PB, byteContent);
            }
            
        }

        public void sendTcpMsg(ushort f_id, string jsonStr)
        {
            tcpConc.SendMessage(f_id, MsgType.JSON, jsonStr != null ? System.Text.Encoding.UTF8.GetBytes(jsonStr) : null);
        }

        List<int> mRequestingMsg = new List<int>();

        public System.Collections.IEnumerator sendTcpRequestMsg(ushort id, string jsonStr, float timeout = 30f, bool showSpinning = true)
        {
            yield return new Framework.GetTid();
            int tid = (int)Framework.CoroutineDecorator.Instance.returnValueRegister;
            mRequestingMsg.Add(tid);
            byte[] content = System.Text.Encoding.UTF8.GetBytes(jsonStr);
            byte[] payload = new byte[content.Length + 4];
            byte[] requestIdBytes = System.BitConverter.GetBytes(tid);
            System.Array.Copy(requestIdBytes, 0, payload, 0, 4);
            System.Array.Copy(content, 0, payload, 4, content.Length);
            if(showSpinning)
                Spinning.Instance.SetActive(true);
            tcpConc.SendMessage(id, MsgType.JSON_REQUEST, payload);
            yield return new Framework.Sleep(tid, timeout);
            if(showSpinning)
                Spinning.Instance.SetActive(false);
            mRequestingMsg.Remove(tid);
        }

        void ontcpDisconnect()
        {
            for(int i = mRequestingMsg.Count - 1; i >= 0; i--)
            {
                int tid = mRequestingMsg[i];
                mRequestingMsg.RemoveAt(i);
                Framework.CoroutineDecorator.Instance.Callback(tid, null);
            }
        }

        public System.Collections.IEnumerator sendKcpRequestMsg(ushort id, string jsonStr, float timeout = -1)
        {
            yield return new Framework.GetTid();
            int tid = (int)Framework.CoroutineDecorator.Instance.returnValueRegister;
            byte[] content = System.Text.Encoding.UTF8.GetBytes(jsonStr);
            byte[] payload = new byte[content.Length + 4];
            byte[] requestIdBytes = System.BitConverter.GetBytes(tid);
            System.Array.Copy(requestIdBytes, 0, payload, 0, 4);
            System.Array.Copy(content, 0, payload, 4, content.Length);
            kcpConc.SendMessage(id, MsgType.JSON_REQUEST, payload);
            yield return new Framework.Sleep(tid, timeout);
        }
    }
}
